#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "train_ticket.h"
#include "list.h"
#include "input_judge.h"
#include "log.h"
#include "core.h"

static int save_train_ticket(struct list_head *p_ticket_head);
static int load_train_ticket(struct list_head *p_ticket_head);
static struct train_ticket *locate_train_ticket(struct list_head *p_ticket_head, char *id);
static int save_user_info(struct user_info *p_user_info, struct list_head *p_booked_ticket_id_head);
static int encipher_or_decipher(char *data, int data_length);
static int file_lock_set(FILE *stream, int type);


/**
 * @brief	火车订票系统初始化          
 * @param	p_ticket_head               : 指向车票链表
 *          p_booked_ticket_id_head     ：指向用户车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
int train_ticket_init(struct list_head *p_ticket_head, 
                      struct list_head *p_booked_ticket_id_head)
{
    int ret, count;
    FILE *fp_train_ticket = NULL;
    char *data;
    int data_length;
    struct train_ticket train_ticket_data[6] = {
        {"d1111", "惠州", "佛山", "09:00", "11:00", "100", "50", "50"},
        {"d2222", "佛山", "惠州", "09:00", "11:00", "100", "50", "50"},
        {"d3333", "惠州", "揭阳", "09:00", "11:00", "100", "50", "50"},
        {"d4444", "揭阳", "惠州", "09:00", "11:00", "100", "50", "50"},
        {"d5555", "大江", "回家", "09:00", "11:00", "100", "50", "50"},
        {"d5555", "回家", "大江", "09:00", "11:00", "100", "50", "50"}
    };
    struct train_ticket encipher_data[6];

    if (!p_ticket_head || 
        !p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    if (access(LIB_DIR, F_OK) != 0)
        mkdir(LIB_DIR, 0777);

    if (access(TRAIN_TICKET_FILE, F_OK) != 0) {
        fp_train_ticket = fopen(TRAIN_TICKET_FILE, "ab+");
        if (!fp_train_ticket) {
            DEBUG("加载车票失败,文件打开失败\n");
            return (-TICKET_OPEN_FILE_FAIL);
        }
        file_lock_set(fp_train_ticket, F_WRLCK);

        for (count = 0; count < 6; count++) {
            /*将要写入文件的内容加密*/
            memcpy(&encipher_data[count], &train_ticket_data[count], sizeof(struct train_ticket));
            data = (char *)&encipher_data[count];
            data_length = sizeof(struct train_ticket);
            ret = encipher_or_decipher(data, data_length);
            if (ret < 0) {
                DEBUG("车票信息加密失败\n");
                return (-TRAIN_ENCIPHER_FAIL);
            }

            if (fwrite(&encipher_data[count], 
                       sizeof(struct train_ticket), 
                       1, 
                       fp_train_ticket) != 1) {
                DEBUG("fwrite error");
                file_lock_set(fp_train_ticket, F_UNLCK);
                fclose(fp_train_ticket);
                return (-TICKET_WRITE_FILE_FAIL);
            }
        }

        file_lock_set(fp_train_ticket, F_UNLCK);
        fclose(fp_train_ticket);
    }

    init_list_head(p_ticket_head);
    ret = load_train_ticket(p_ticket_head);
    
    init_list_head(p_booked_ticket_id_head);

    return ret;
}

/**
 * @brief	添加车票（给管理员用）       
 * @param	p_ticket_head       : 指向车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
int add_train_ticket(struct list_head *p_ticket_head, struct train_ticket train_ticket_data)
{
    int8_t ret = 0;
    struct train_ticket *p_train_ticket_data;

    if (!p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    /*判断id格式*/
    ret = is_ticket_id(train_ticket_data.id);
    if (ret == FALSE) {
        printf("车票id格式错误！\n");
        return FALSE;
    }

    /*判断id是否存在*/
    if (locate_train_ticket(p_ticket_head, train_ticket_data.id) != NULL) {
        printf("车票id已存在！\n");
        return FALSE;
    }

    /*判断出发点格式*/
    ret = is_chinese(train_ticket_data.start_city);
    if (ret == FALSE) {
        printf("出发点格式错误,必须为中文！\n");
        return FALSE;
    }

    /*判断终点格式*/
    ret = is_chinese(train_ticket_data.end_city);
    if (ret == FALSE) {
        printf("终点格式错误,必须为中文！\n");
        return FALSE;
    }

    /*判断发车时间格式*/
    ret = is_time(train_ticket_data.departure_time);
    if (ret == FALSE) {
        printf("发车时间格式错误,必须为xx:xx！\n");
        return FALSE;
    }

    /*判断到达时间格式*/
    ret = is_time(train_ticket_data.arrival_time);
    if (ret == FALSE) {
        printf("到达时间格式错误,必须为xx:xx！\n");
        return FALSE;
    }
  
    /*判断票价格式*/
    ret = is_num(train_ticket_data.ticket_price);
    if (ret == FALSE) {
        printf("车票价格格式错误！\n");
        return FALSE;
    }

    /*判断余票格式*/
    ret = is_num(train_ticket_data.spare_ticket_num);
    if (ret == FALSE) {
        printf("余票数量格式错误！\n");
        return FALSE;
    }
    /*判断总票数格式*/
    ret = is_num(train_ticket_data.total_ticket_num);
    if (ret == FALSE) {
        printf("总票数量格式错误！\n");
        return FALSE;
    }

    p_train_ticket_data = (struct train_ticket *)malloc(sizeof(struct train_ticket));

    strcpy(p_train_ticket_data->id, train_ticket_data.id);
    strcpy(p_train_ticket_data->start_city, train_ticket_data.start_city);
    strcpy(p_train_ticket_data->end_city, train_ticket_data.end_city);
    strcpy(p_train_ticket_data->departure_time, train_ticket_data.departure_time);
    strcpy(p_train_ticket_data->arrival_time, train_ticket_data.arrival_time);
    strcpy(p_train_ticket_data->ticket_price, train_ticket_data.ticket_price);
    strcpy(p_train_ticket_data->spare_ticket_num, train_ticket_data.spare_ticket_num);
    strcpy(p_train_ticket_data->total_ticket_num, train_ticket_data.total_ticket_num);
    init_list_head(&p_train_ticket_data->list);

    list_add_tail(&p_train_ticket_data->list, p_ticket_head);

    ret = save_train_ticket(p_ticket_head);
    if (ret != TRUE) {
        DEBUG("保存车票信息失败\n");
        return FALSE;
    }

    LOG("添加车票%s", p_train_ticket_data->id);
    return TRUE;
}

/**
 * @brief	删除车票（给管理员用）       
 * @param	p_ticket_head       : 指向车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
int delete_train_ticket(struct list_head *p_ticket_head)
{
    struct list_head *next,*head;
    struct train_ticket *p_train_ticket_data = NULL;
    int8_t ret = 0;
    char input[INPUT_LENGTH];
    char id[TICKET_ID_LENGTH];

    if (!p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    memset(input, '\0', INPUT_LENGTH);
    memset(id, '\0', TICKET_ID_LENGTH);

    //输入车票id
    while (1) {
        printf("请输入车票id：");
        get_str_input(input);
        ret = is_ticket_id(input);
        if (ret == FALSE) {
            printf("车票id输入错误！\n");
            printf("格式为第一个为小写英文字母，后面接4个数字.eg:a1234\n");
            continue;
        }

        strcpy(id, input);
        break;
    }

    //搜索对应id的车票
    head = p_ticket_head;
    ret = FALSE;
    list_for_each(next, head) {

        p_train_ticket_data = list_entry(next, struct train_ticket, list);
        if (!strcmp(p_train_ticket_data->id, id)) {
            list_del(&p_train_ticket_data->list);
            next = head;
            free(p_train_ticket_data);
            ret = TRUE;
            break;
        }
    }

    if(ret == TRUE)
        save_train_ticket(p_ticket_head);

    LOG("删除车票%s", id);
    return ret;
}

/**
 * @brief	搜索车票       
 * @param	p_ticket_head       : 指向车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
struct train_ticket *search_train_ticket(struct list_head *p_ticket_head)
{
    struct list_head *next,*head;
    struct train_ticket *p_train_ticket_data = NULL;
    int8_t ret = 0;
    int length = 0;
    char input[INPUT_LENGTH];
    char start_city[CITY_LENGTH];
    char end_city[CITY_LENGTH];

    memset(input, '\0', INPUT_LENGTH);
    memset(start_city, '\0', CITY_LENGTH);
    memset(end_city, '\0', CITY_LENGTH);

    if (!p_ticket_head) {
        printf("PARAM ERROR!\n");
        return NULL;
    }

    //输入出发城市
    while (1) {
        printf("请输入出发城市：");
        get_str_input(input);
        length = strlen(input);
        if (length > CITY_LENGTH - 1) {
            printf("出发城市输入错误！\n");
            continue;
        }

        ret = is_chinese(input);
        if (ret == FALSE) {
            printf("出发城市输入错误，请输入城市中文名!\n");
            continue;
        }

        strcpy(start_city, input);
        break;
    }

    //输入终点城市
    memset(input, '\0', INPUT_LENGTH);
    while (1) {
        printf("请输入终点城市：");
        get_str_input(input);
        length = strlen(input);
        if (length > CITY_LENGTH - 1) {
            printf("终点城市输入错误\n");
            continue;
        }

        ret = is_chinese(input);
        if (ret == FALSE) {
            printf("终点城市输入错误，请输入城市中文名!\n");
            continue;
        }

        strcpy(end_city, input);
        break;
    }

    //搜索对应起点和终点的车票
    head = p_ticket_head;
    ret = FALSE;
    list_for_each(next, head) {

        p_train_ticket_data = list_entry(next, struct train_ticket, list);
        if (!strcmp(p_train_ticket_data->start_city, start_city) &&
            !strcmp(p_train_ticket_data->end_city, end_city)) {
                ret = TRUE;
                break;
            }
    }

    if(ret == TRUE)
        return p_train_ticket_data;

    return NULL;
}

/**
 * @brief	显示所有车票      
 * @param	p_ticket_head       : 指向车票链表
 * @param	page                ：打印第几页
 * @param	num                 : 一页的票数
 * @return	成功返回1，失败返回0，出错返回负值
 */
int display_train_ticket(struct list_head *p_ticket_head, int page, int num)
{
    struct list_head *next, *head;
    int count = 0, ticket_num = 0;
    struct train_ticket *p_train_ticket_data = NULL;

    if (!p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    if (list_empty(p_ticket_head)) {
        printf("亲爱的旅客，现在还没有票卖，请通知管理员添加车票！\n");
        return TRUE;
    }

    /*获取车票数量*/
    head = p_ticket_head;
    list_for_each(next, head) {
        ticket_num++;
    }

    printf("  车票ID  起点  终点  发车  到站  价格  余/总票数");
    MOVEDOWN(2);
    MOVELEFT(46);

    /*根据start 和 end 来打印对应的车票*/
    list_for_each(next, head) {
        p_train_ticket_data = list_entry(next, struct train_ticket, list);
        if((count >= ((page - 1) * num) && count < ( page * num))) {
            printf("%s  %s  %s  %s %s  %s    %s/%s", 
                p_train_ticket_data->id, p_train_ticket_data->start_city,
                p_train_ticket_data->end_city, p_train_ticket_data->departure_time,
                p_train_ticket_data->arrival_time, p_train_ticket_data->ticket_price,
                p_train_ticket_data->spare_ticket_num, p_train_ticket_data->total_ticket_num);
            /*换行*/
            MOVEDOWN(2);
            MOVELEFT((int)(strlen(p_train_ticket_data->id) + 
                        strlen(p_train_ticket_data->start_city) +
                        strlen(p_train_ticket_data->end_city) +
                        strlen(p_train_ticket_data->departure_time) +
                        strlen(p_train_ticket_data->arrival_time) +
                        strlen(p_train_ticket_data->ticket_price) +
                        strlen(p_train_ticket_data->spare_ticket_num) +
                        strlen(p_train_ticket_data->total_ticket_num))+10);
        }
        count++;
    }

    return TRUE;
}

/**
 * @brief	保存所有车票，写进文件      
 * @param	p_ticket_head       : 指向车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
static int save_train_ticket(struct list_head *p_ticket_head)
{   
    struct list_head *next, *head;
    struct train_ticket *p_train_ticket_data = NULL;
    struct train_ticket encipher_data;
    FILE *fp_train_ticket = NULL;
    char *data;
    int data_length, ret;

    if (!p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    fp_train_ticket = fopen(TRAIN_TICKET_FILE, "wb");
    if (!fp_train_ticket) {
        DEBUG("文件打开失败\n");
        return (-TICKET_OPEN_FILE_FAIL);
    }
    file_lock_set(fp_train_ticket, F_WRLCK);

    head = p_ticket_head;
    list_for_each(next, head) {
        p_train_ticket_data = list_entry(next, struct train_ticket, list);

        /*将要写入文件的内容加密*/
        memcpy(&encipher_data, p_train_ticket_data, sizeof(struct train_ticket));
        data = (char *)&encipher_data;
        data_length = sizeof(struct train_ticket);
        ret = encipher_or_decipher(data, data_length);
        if (ret < 0) {
            DEBUG("车票信息加密失败\n");
            return (-TRAIN_ENCIPHER_FAIL);
        }

        if (fwrite(&encipher_data, sizeof(struct train_ticket), 1, fp_train_ticket) != 1)
            DEBUG("fwrite error");
    }

    file_lock_set(fp_train_ticket, F_UNLCK);
    fclose(fp_train_ticket);

    return TRUE;
}

/**
 * @brief	加载所有车票，从文件中加载      
 * @param	p_ticket_head       : 指向车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
static int load_train_ticket(struct list_head *p_ticket_head)
{
    FILE *fp_train_ticket = NULL;
    struct train_ticket *p_train_ticket_next;
    char *data;
    int data_length, ret;
    
    if (!p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    fp_train_ticket = fopen(TRAIN_TICKET_FILE, "ab+");
    if (!fp_train_ticket) {
        DEBUG("加载车票失败,文件打开失败\n");
        return (-TICKET_OPEN_FILE_FAIL);
    }
    file_lock_set(fp_train_ticket, F_WRLCK);

    while (!feof(fp_train_ticket)) {
        p_train_ticket_next = (struct train_ticket *)malloc(sizeof(struct train_ticket));
        if (fread(p_train_ticket_next, 
                  sizeof(struct train_ticket), 1, fp_train_ticket) == 1) {

            /*将从文件中读取的内容解密*/
            data = (char *)p_train_ticket_next;
            data_length = sizeof(struct train_ticket);
            ret = encipher_or_decipher(data, data_length);
            if (ret < 0) {
                DEBUG("车票信息解密失败\n");
                return (-TRAIN_DECIPHER_FAIL);
            }

            list_add_tail(&p_train_ticket_next->list, p_ticket_head);
        } else {
            free(p_train_ticket_next);
        }

    }

    file_lock_set(fp_train_ticket, F_UNLCK);
    fclose(fp_train_ticket);
    return TRUE;

}

/**
 * @brief	定位某张车票     
 * @param	p_ticket_head       : 指向车票链表
 *          id                  ：要查找的车票信息的车票id
 * @return	成功返回相应车票的信息，失败返回NULL
 */
static struct train_ticket *locate_train_ticket(struct list_head *p_ticket_head, char *id)
{
    struct list_head *next, *head;
    struct train_ticket *p_train_ticket_data = NULL;

    if (!p_ticket_head || !id) {
        DEBUG("PARAM ERROR!\n");
        return NULL;
    }

    head = p_ticket_head;
    list_for_each(next, head) {
        p_train_ticket_data = list_entry(next, struct train_ticket, list);
        if(!strcmp(p_train_ticket_data->id, id))
            return p_train_ticket_data;
    }

    return NULL;
}

/**
 * @brief	加载用户信息，从文件中加载     
 * @param	account                         : 要加载的用户的账号
 *          p_user_info                     ：指向保存用户信息的结构体
 *          p_booked_ticket_id_head         ：指向用户的车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
int load_user_info(char *account, 
                   struct user_info *p_user_info, 
                   struct list_head *p_booked_ticket_id_head)
{
    uint8_t count;
    struct list_head *next = NULL;
    struct list_head *head = NULL;
    struct user_info user_info_data;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;
    FILE *fp_user_info = NULL;
    char *data;
    int data_length, ret;

    if (!account || !p_user_info || !p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    head = p_booked_ticket_id_head;
    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        list_del(&p_booked_ticket_id_data->list);
        next = head;
        free(p_booked_ticket_id_data);
    }

    memset(&user_info_data, '\0', sizeof(struct user_info));

    fp_user_info = fopen(USER_INFO_FILE, "ab+");
    if (!fp_user_info) {
        DEBUG("文件打开失败\n");
        return (-TICKET_OPEN_FILE_FAIL);
    }
    file_lock_set(fp_user_info, F_WRLCK);

    while (!feof(fp_user_info)) {
        if (fread(&user_info_data, sizeof(struct user_info), 1, fp_user_info)) {

            /*将从文件中读取的内容解密*/
            data = (char *)&user_info_data;
            data_length = sizeof(struct user_info);
            ret = encipher_or_decipher(data, data_length);
            if (ret < 0) {
                DEBUG("用户信息解密失败\n");
                return (-TRAIN_DECIPHER_FAIL);
            }

            //找到对应账号的信息
            if (!strcmp(user_info_data.account, account)) {
                *p_user_info = user_info_data;
                for (count = 0; count < OWN_TICKET_MAX; count++) {
                    if (is_ticket_id((char *)p_user_info->own_ticket[count])) {
                        p_booked_ticket_id_data = (struct booked_ticket_id *)
                                                  malloc(sizeof(struct booked_ticket_id));
                        list_add_tail(&p_booked_ticket_id_data->list, p_booked_ticket_id_head);
                        strcpy(p_booked_ticket_id_data->id, 
                               p_user_info->own_ticket[count]);
                    } else 
                        break;
                }    

                memset(p_user_info->own_ticket, '\0', OWN_TICKET_MAX*TICKET_ID_LENGTH);
                file_lock_set(fp_user_info, F_UNLCK);
                fclose(fp_user_info);
                return TRUE;
            }
        }
    }

    file_lock_set(fp_user_info, F_UNLCK);
    fclose(fp_user_info);

    return FALSE;
}

/**
 * @brief	修改用户信息   
 * @param	p_user_info     ：指向保存用户信息的结构体
 *          p_booked_ticket_id_head     ：指向用户购买的车票id的链表头
 * @return	成功返回1，失败返回0，出错返回负值
 */
int change_user_info(struct user_info *p_user_info,
                     struct list_head *p_booked_ticket_id_head)
{
    int8_t ret = 0;
    int length;

    if (!p_user_info || !p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    if (strlen(p_user_info->age) == 0 ||
        strlen(p_user_info->sex) == 0 ||
        strlen(p_user_info->phone_num) == 0 ||
        strlen(p_user_info->home_address) == 0) {
        printf("输入信息不能为空！");
        return FALSE;
    }

    /*判断电话号码格式*/
    length = strlen(p_user_info->phone_num);
    if (length != PHONE_NUN_LENGTH - 1) {
        printf("手机号输入错误，长度应该为11位！\n");
        return FALSE;
    }
    ret = is_num(p_user_info->phone_num);
    if (ret == FALSE) {
        printf("手机号格式错误，手机号只有数字组成！\n");
        return FALSE;
    }

    /*判断家庭住址格式*/
    ret = is_num_or_char(p_user_info->home_address);
    if (ret == FALSE) {
        printf("家庭住址格式错误，由英文和数字组成！\n");
        return FALSE;
    }

    /*判断年龄格式*/
    ret = is_num(p_user_info->age);
    if (ret == FALSE) {
        printf("年龄格式错误，由数字组成！\n");
        return FALSE;
    }

    /*判断性别格式*/
    if (p_user_info->sex[0] != 'M' && 
        p_user_info->sex[0] != 'F' &&
        p_user_info->sex[0] != 'f' &&
        p_user_info->sex[0] != 'm') {
        printf("性别输入错误，请输入M、m 或者 F、f！\n");
        return FALSE;
    }
    
    p_user_info->is_have_info = 1;
    save_user_info(p_user_info, p_booked_ticket_id_head);
    
    return TRUE;
}

/**
 * @brief	添加用户信息     
 * @param	p_user_info     ：指向保存用户信息的结构体
 *          name            ：用户名（昵称）
 *          account         ：用户账号
 * @return	成功返回1，失败返回0，出错返回负值
 */
int add_user_info(struct user_info *p_user_info, char *name, char *account)
{
    int8_t ret = 0;
    int length;
    struct list_head booked_ticket_id_head;

    if (!p_user_info || !name || !account) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    init_list_head(&booked_ticket_id_head);

    if (strlen(p_user_info->age) == 0 ||
        strlen(p_user_info->sex) == 0 ||
        strlen(p_user_info->phone_num) == 0 ||
        strlen(p_user_info->home_address) == 0) {
        printf("输入信息不能为空！");
        return FALSE;
    }

    /*获取用户昵称*/
    strcpy(p_user_info->name, name);
    /*获取用户账号*/
    strcpy(p_user_info->account, account);


    /*判断电话号码格式*/
    length = strlen(p_user_info->phone_num);
    if (length != PHONE_NUN_LENGTH - 1) {
        printf("手机号输入错误，长度应该为11位！\n");
        return FALSE;
    }
    ret = is_num(p_user_info->phone_num);
    if (ret == FALSE) {
        printf("手机号格式错误，手机号只有数字组成！\n");
        return FALSE;
    }

    /*判断家庭住址格式*/
    ret = is_num_or_char(p_user_info->home_address);
    if (ret == FALSE) {
        printf("家庭住址格式错误，由英文和数字组成！\n");
        return FALSE;
    }

    /*判断年龄格式*/
    ret = is_num(p_user_info->age);
    if (ret == FALSE) {
        printf("年龄格式错误，由数字组成！\n");
        return FALSE;
    }

    /*判断性别格式*/
    if (p_user_info->sex[0] != 'M' && 
        p_user_info->sex[0] != 'F' &&
        p_user_info->sex[0] != 'f' &&
        p_user_info->sex[0] != 'm') {
        printf("性别输入错误，请输入M、m 或者 F、f！\n");
        return FALSE;
    }
    

    p_user_info->own_ticket_count = 0;
    memset(p_user_info->own_ticket, '\0', OWN_TICKET_MAX*TICKET_ID_LENGTH);
    p_user_info->is_have_info = 1;
    save_user_info(p_user_info, &booked_ticket_id_head);
    
    return TRUE;
}

int logout_user_info(struct user_info *p_user_info, 
                     struct list_head *p_booked_ticket_id_head)
{
    struct list_head *next, *head;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;

    if (!p_user_info || !p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    /*清除前保存**/
    save_user_info(p_user_info, p_booked_ticket_id_head);

    p_user_info->is_have_info = 0;
    p_user_info->own_ticket_count = 0;
    memset(p_user_info->sex, '\0', 2);
    memset(p_user_info->age, '\0', 4);
    memset(p_user_info->name, '\0', USER_NAME_LENGTH);
    memset(p_user_info->account, '\0', USER_ACCOUNT_LENGTH);
    memset(p_user_info->phone_num, '\0', PHONE_NUN_LENGTH);
    memset(p_user_info->home_address, '\0', HOME_ADDRESS_LENGTH);

    head = p_booked_ticket_id_head;
    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        list_del(&p_booked_ticket_id_data->list);
        next = head;
        free(p_booked_ticket_id_data);
    }

    return TRUE;
}

/**
 * @brief	保存用户信息，保存到文件
 * @param	p_user_info                 ：指向保存用户信息的结构体
 *          p_booked_ticket_id_head     ：指向用户车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
static int save_user_info(struct user_info *p_user_info, 
                          struct list_head *p_booked_ticket_id_head)
{
    FILE *fp_user_info = NULL;
    struct list_head *next, *head;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;
    uint8_t count = 0;
    struct user_info user_info_data;
    struct user_info encipher_data;
    long int offset = 0;
    char *data;
    int data_length, ret;

    if (!p_user_info || !p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    fp_user_info = fopen(USER_INFO_FILE, "ab+");
    if (!fp_user_info) {
        DEBUG("保存用户信息失败,文件打开失败\n");
        return (-TICKET_OPEN_FILE_FAIL);
    }
    file_lock_set(fp_user_info, F_WRLCK);

    head = p_booked_ticket_id_head;
    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        strcpy(p_user_info->own_ticket[count], 
               p_booked_ticket_id_data->id);
        count++;
    }

    while (!feof(fp_user_info)) {
        if (fread(&user_info_data, sizeof(struct user_info), 1, fp_user_info)) {

            /*将从文件中读取的内容解密*/
            data = (char *)&user_info_data;
            data_length = sizeof(struct user_info);
            ret = encipher_or_decipher(data, data_length);
            if (ret < 0) {
                DEBUG("用户信息解密失败\n");
                return (-TRAIN_DECIPHER_FAIL);
            }

            if (!strcmp(user_info_data.account, p_user_info->account)) {
                fseek(fp_user_info, -sizeof(struct user_info), SEEK_CUR);
                offset = ftell(fp_user_info);
                break;
            }
        }
        offset = ftell(fp_user_info);
    }

    file_lock_set(fp_user_info, F_UNLCK);
    fclose(fp_user_info);

    fp_user_info = fopen(USER_INFO_FILE, "rb+");
    if (!fp_user_info) {
        DEBUG("保存用户信息失败,文件打开失败\n");
        return (-TICKET_OPEN_FILE_FAIL);
    }
    file_lock_set(fp_user_info, F_WRLCK);

    fseek(fp_user_info, offset, SEEK_SET);
    /*将要写入文件的内容加密*/
    memcpy(&encipher_data, p_user_info, sizeof(struct user_info));
    data = (char *)&encipher_data;
    data_length = sizeof(struct user_info);
    ret = encipher_or_decipher(data, data_length);
    if (ret < 0) {
        DEBUG("用户信息加密失败\n");
        return (-TRAIN_ENCIPHER_FAIL);
    }

    if (fwrite(&encipher_data, sizeof(struct user_info), 1, fp_user_info) != 1) {
        DEBUG("fwrite error");
        memset(p_user_info->own_ticket, '\0', OWN_TICKET_MAX*TICKET_ID_LENGTH);
        file_lock_set(fp_user_info, F_UNLCK);
        fclose(fp_user_info);
        return (-TICKET_WRITE_FILE_FAIL);
    }
    
    file_lock_set(fp_user_info, F_UNLCK);
    fclose(fp_user_info);

    memset(p_user_info->own_ticket, '\0', OWN_TICKET_MAX*TICKET_ID_LENGTH);

    return TRUE;
}

/**
 * @brief	打印用户购买的车票ID
 * @param	p_booked_ticket_id_head  ：指向用户购买车票ID信息的链表头
 * @return	成功返回0，出错返回负值
 */

int display_user_ticket_id(struct list_head *p_booked_ticket_id_head)
{
    struct list_head *next, *head;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;

    if (!p_booked_ticket_id_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }
    
    head = p_booked_ticket_id_head;
    if (list_empty(p_booked_ticket_id_head)) {
        MOVEDOWN(1);
        printf("暂无...");
        return FALSE;
    }

    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        printf("%s", p_booked_ticket_id_data->id);
        MOVEDOWN(1);
        MOVELEFT((int)strlen(p_booked_ticket_id_data->id));
    }
    
    return TRUE;
}

/**
 * @brief	打印用户购买车票信息
 * @param	p_booked_ticket_id_head     ：指向用户车票链表
 *          p_ticket_head               ：指向所有车票链表
 * @return	成功返回1，失败返回0，出错返回负值
 */
int display_user_ticket(struct list_head *p_booked_ticket_id_head, 
                        struct list_head *p_ticket_head)
{
    struct train_ticket *p_train_ticket_data = NULL;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;
    struct list_head *next, *head;

    if (!p_booked_ticket_id_head || !p_ticket_head) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    printf("        亲爱的旅客,您已购买的车票信息：");
    MOVEDOWN(1);
    MOVELEFT(31);
    printf("车票ID  起点  终点  发车  到站  票价");
    MOVEDOWN(2);
    MOVELEFT(36);
    if (list_empty(p_booked_ticket_id_head)) {
        printf("当前用户无购买车票！");
        return FALSE;
    }

    head = p_booked_ticket_id_head;
    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        p_train_ticket_data = locate_train_ticket(p_ticket_head, p_booked_ticket_id_data->id);
        printf("%s   %s  %s  %s %s  %s", 
               p_train_ticket_data->id, p_train_ticket_data->start_city,
               p_train_ticket_data->end_city, p_train_ticket_data->departure_time,
               p_train_ticket_data->arrival_time, p_train_ticket_data->ticket_price);
        MOVEDOWN(2);
        MOVELEFT((int)(strlen(p_train_ticket_data->id) + 
                    strlen(p_train_ticket_data->start_city) +
                    strlen(p_train_ticket_data->end_city) +
                    strlen(p_train_ticket_data->departure_time) +
                    strlen(p_train_ticket_data->arrival_time) +
                    strlen(p_train_ticket_data->ticket_price)) + 6);
    }
    return TRUE;
}


/**
 * @brief	买票
 * @param	p_user_info                 ：指向用户信息的结构体
 *          p_booked_ticket_id_head     ：指向用户车票链表
 *          p_ticket_head               ：指向所有车票链表
 *          id                          ：用户id
 * @return	成功返回1，失败返回0，出错返回负值
 */
int book_train_ticket(struct user_info *p_user_info, 
                      struct list_head *p_booked_ticket_id_head, 
                      struct list_head *p_ticket_head, 
                      char *id)
{
    int ret;
    int temp_spare_ticket_num;
    struct train_ticket *p_train_ticket_data = NULL;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;

    if (!p_user_info ||
        !p_booked_ticket_id_head ||
        !p_ticket_head ||
        !id ) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }


    ret = is_ticket_id(id);
    if (ret == FALSE) {
        printf("车票id格式错误！\n");
        return FALSE;
    }
    
    p_train_ticket_data = locate_train_ticket(p_ticket_head, id);
    if (!p_train_ticket_data) {
        printf("车票不存在\n");
        return FALSE;
    }

    temp_spare_ticket_num = atoi(p_train_ticket_data->spare_ticket_num);

    if (temp_spare_ticket_num == 0) {
        printf("票卖光了！\n");
        return FALSE;
    }

    if (p_user_info->own_ticket_count >= OWN_TICKET_MAX) {
        printf("买不了，你最多只能买5张票！\n");
        return FALSE;
    }

    p_booked_ticket_id_data = (struct booked_ticket_id *)malloc(sizeof(struct booked_ticket_id));
    list_add_tail(&p_booked_ticket_id_data->list, p_booked_ticket_id_head);
    strcpy(p_booked_ticket_id_data->id, id);

    temp_spare_ticket_num--;
    p_user_info->own_ticket_count++;

    sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);

    ret = save_train_ticket(p_ticket_head);
    if (ret != TRUE) {
        DEBUG("车票信息更新失败\n");
        temp_spare_ticket_num++;
        p_user_info->own_ticket_count--;
         sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);
        return FALSE;
    }

    ret = save_user_info(p_user_info, p_booked_ticket_id_head);
    if (ret != TRUE) {
        DEBUG("用户信息更新失败\n");
        temp_spare_ticket_num++;
        p_user_info->own_ticket_count--;
        sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);
        return FALSE;
    }

    LOG("用户%s购买车票%s", p_user_info->name, id);
    return TRUE;
}

/**
 * @brief	退票
 * @param	p_user_info                 ：指向用户信息的结构体
 *          p_booked_ticket_id_head     ：指向用户车票链表
 *          p_ticket_head               ：指向所有车票链表
 *          id                          ：用户id
 * @return	成功返回1，失败返回0，出错返回负值
 */
int return_train_ticket(struct user_info *p_user_info, 
                        struct list_head *p_booked_ticket_id_head, 
                        struct list_head *p_ticket_head, 
                        char *id)
{
    int8_t ret = 0;
    int temp_spare_ticket_num;
    struct list_head *next = NULL;
    struct list_head *head = NULL;
    struct train_ticket *p_train_ticket_data = NULL;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;

    if (!p_user_info ||
        !p_booked_ticket_id_head || 
        !p_ticket_head || 
        !id) {
        DEBUG("PARAM ERROR!\n");
        return (-TICKET_PARAM_NULL);
    }

    ret = is_ticket_id(id);
    if (ret == FALSE)
        return FALSE;

    p_train_ticket_data = locate_train_ticket(p_ticket_head, id);
    temp_spare_ticket_num = atoi(p_train_ticket_data->spare_ticket_num);
    if (!p_train_ticket_data) {
        printf("车票不在列表！\n");
        return FALSE;
    }

    if (p_user_info->own_ticket_count == 0) {
        printf("当前用户没有购买车票!\n");
        return FALSE;
    }

    head = p_booked_ticket_id_head;
    ret = FALSE;
    list_for_each(next, head) {
        p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
        if (!strcmp(p_booked_ticket_id_data->id, id)) {
            list_del(&p_booked_ticket_id_data->list);
            next = head;
            free(p_booked_ticket_id_data);
            ret = TRUE;
            break;
        }
    }

    if (ret == TRUE) {
        p_user_info->own_ticket_count--;
        temp_spare_ticket_num++;
        sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);

        ret = save_train_ticket(p_ticket_head);
        if (ret != TRUE) {
            DEBUG("车票信息更新失败\n");
            temp_spare_ticket_num--;
            p_user_info->own_ticket_count++;
            sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);
            return FALSE;
        }

        ret = save_user_info(p_user_info, p_booked_ticket_id_head);
        if (ret != TRUE) {
            DEBUG("用户信息更新失败\n");
            temp_spare_ticket_num--;
            sprintf(p_train_ticket_data->spare_ticket_num, "%d", temp_spare_ticket_num);
            p_user_info->own_ticket_count++;
            return FALSE;
        }

    } else {
        printf("没有购买这张车票\n");
        return FALSE;
    }

    LOG("用户%s退车票%s", p_user_info->name, id);
    return TRUE;
}

/**
 * @brief	系统退出前，用来刷新所有用户信息，车票信息，将其保存到文件中
 * @param   p_ticket_head               ：指向所有车票链表
 * 	        p_user_info                 ：指向用户信息的结构体
 *          p_booked_ticket_id_head     ：指向用户车票链表
 * @return	成功返回1
 */
int train_ticket_destroy(struct list_head *p_ticket_head, 
                         struct user_info *p_user_info, 
                         struct list_head *p_booked_ticket_id_head)
{
    struct list_head *next = NULL;
    struct list_head *head = NULL;
    struct train_ticket *p_train_ticket_data = NULL;
    struct booked_ticket_id *p_booked_ticket_id_data = NULL;

    if (p_ticket_head != NULL) {
        save_train_ticket(p_ticket_head);

        head = p_ticket_head;
        list_for_each(next, head) {
            p_train_ticket_data = list_entry(next, struct train_ticket, list);
            list_del(&p_train_ticket_data->list);
            next = head;
            free(p_train_ticket_data);
        }
    }

    if (p_user_info != NULL) {
        save_user_info(p_user_info, p_booked_ticket_id_head);

        head = p_booked_ticket_id_head;
        list_for_each(next, head) {
            p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
            list_del(&p_booked_ticket_id_data->list);
            next = head;
            free(p_booked_ticket_id_data);
        }
    } else {
        head = p_booked_ticket_id_head;
        list_for_each(next, head) {
            p_booked_ticket_id_data = list_entry(next, struct booked_ticket_id, list);
            list_del(&p_booked_ticket_id_data->list);
            next = head;
            free(p_booked_ticket_id_data);
        }
    }

    return TRUE;
}

static int encipher_or_decipher(char *data, int data_length)
{
	uint8_t count;
	
	if (!data) {
        DEBUG("PARAM ERROR!\n");
        return -1;
    }
	
    for(count = 0; count < data_length; count++) {
        if(data[count] != '\0')
			data[count]^='!';
    }

    return 0;
}

int file_lock_set(FILE *stream,int type)
{
    int fd;
    struct flock lock;

	lock.l_type = type;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_pid = -1;

    fd = fileno(stream);	

	fcntl(fd,F_GETLK,&lock);

	if (lock.l_type != F_UNLCK) {
		if (lock.l_type == F_RDLCK)  
			DEBUG("Read lock already set by %d\n",lock.l_pid);

		if (lock.l_type == F_WRLCK) 
			DEBUG("Write lock already set by %d\n",lock.l_pid);					
	}

	lock.l_type = type;
    lock.l_pid = getpid();

	if ((fcntl(fd,F_SETLKW,&lock)) < 0) {
		DEBUG("Lock failed : type = %d\n",lock.l_type);
		return -1;
	}

	switch (lock.l_type) {
		case F_RDLCK:
			DEBUG("Read lock set by %d\n", getpid());
		    break;

		case F_WRLCK:
			DEBUG("write lock set by %d\n", getpid());
		    break;

		case F_UNLCK:
			DEBUG("Release lock by %d\n", getpid());
		    break;

		default:
		    break;
	}

	return 1;
}